package com.zhuqi.config;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zhuqi.filter.JwtAndCodeFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.*;

/**
 * @author zhuqi
 * @version 1.0
 * @date 2021/3/26 17:27
 * spring-security 安全配置类
 * 配置 认证管理器 密码匹配器 HTTP请求规则
 */
@Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private JwtAndCodeFilter jwtAndCodeFilter;

    /**
     * 放入 一个用户到认证管理器中  做测试
     *
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("zhangsan")
                .password(passwordEncoder().encode("123456"))
                .authorities("sys:test");
    }

    /**
     * 配置 HTTP 请求规则
     *
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.addFilterBefore(jwtAndCodeFilter, UsernamePasswordAuthenticationFilter.class);
        http.csrf().disable();
        http.formLogin().loginProcessingUrl("/doLogin")
                .successHandler(authenticationSuccessHandler())
                .permitAll();
        http.authorizeRequests().anyRequest().authenticated();
    }

    /**
     * 身份验证成功的处理器
     * 我们将用户信息封装成 JWT 返回给前端
     * @return
     */
    @Bean
    public AuthenticationSuccessHandler authenticationSuccessHandler(){
        return (request,  response,  authentication)->{
            // 获得角色的用户名的权限
            Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
            String username = authentication.getPrincipal().toString();
            ArrayList<String> auths = new ArrayList<>(authorities.size() * 2);
            authorities.forEach(a->auths.add(a.getAuthority()));
            // 生产 JWT 返回给前端
            Map<String, Object> header = new HashMap<>(4);
            // 设置事件
            Date date = new Date();
            // 到期时间
            Calendar calendar = Calendar.getInstance();
            calendar.add(Calendar.HOUR,2);
            header.put("alg", "HS256");
            header.put("typ", "JWT");
            String jwt = JWT.create().withHeader(header)
                    .withIssuedAt(date)
                    .withExpiresAt(calendar.getTime())
                    .withClaim("username", username)
                    .withClaim("auths", auths)
                    .sign(Algorithm.HMAC256("haha"));
            // 将 jwt 返回给前端
            HashMap<String, Object> map = new HashMap<>(8);
            map.put("code",200);
            map.put("msg","登录成功");
            map.put("token",jwt);
            response.setContentType("application/json;charset=utf-8");
            ObjectMapper mapper = new ObjectMapper();
            String data = mapper.writeValueAsString(map);
            PrintWriter writer = response.getWriter();
            try {
                writer.write(data);
            } finally {
                writer.flush();
                writer.close();
            }
        };
    }

    /**
     * 密码编码器
     *
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}
